package org.dru.clay.util.file;

import org.dru.clay.util.functional.CollectionUtils;
import org.dru.clay.util.string.StringUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

/**
 * FileUtils
 * User: joakimd
 * Date: 2013-07-22
 * Time: 13:43
 */
public final class FileUtils {
    private FileUtils() {
    }

    public static File file(final Iterable<String> parts) {
        File result = null;
        for (final String part : parts) {
            result = new File(result, part.replace("\\", File.separator).replace("/", File.separator));
        }
        return result;
    }

    public static boolean isRelative(final File base, final File file) {
        return file != null && (base.equals(file) || isRelative(base, file.getParentFile()));
    }

    public static String relative(final File base, final File file) {
        return file.getPath().substring(base.getPath().length());
    }

    public static String join(final Iterable<File> files) {
        return StringUtils.join(CollectionUtils.transform(files, FileFunctions.path()), File.pathSeparator);
    }

    public static Collection<File> list(final File dir) {
        if (!dir.isAbsolute() || !dir.isDirectory()) {
            throw new IllegalArgumentException("not an absolute file to a directory: " + dir.getPath());
        }
        final File[] files = dir.listFiles();
        return (files != null ? Arrays.asList(files) : Collections.<File>emptyList());
    }

    public static Collection<File> tree(final File dir) {
        final Collection<File> result = new ArrayList<File>();
        for (final File file : list(dir)) {
            result.add(file);
            if (file.isDirectory()) {
                result.addAll(tree(file));
            }
        }
        return result;
    }

    public static Collection<File> fileList(final File dir) {
        return CollectionUtils.filter(list(dir), FilePredicates.isFile());
    }

    public static Collection<File> fileTree(final File dir) {
        return CollectionUtils.filter(tree(dir), FilePredicates.isFile());
    }

    public static Collection<File> copy(final File from, final File into,
                                        final Iterable<File> files) throws IOException {
        if (!from.isAbsolute() || !from.isDirectory()) {
            throw new Error("from must be an absolute file to a directory: " + from);
        }
        if (!into.isAbsolute() || into.isFile()) {
            throw new Error("into must be an absolute file to a directory: " + into);
        }
        final Collection<File> result = new ArrayList<File>();
        if (!CollectionUtils.every(files, FilePredicates.isRelativeOf(from))) {
            throw new IllegalArgumentException("non-relative files");
        }
        for (final File source : files) {
            final String name = relative(from, source);
            final File destination = new File(into, name);
            if (!destination.exists() ||
                    source.lastModified() != destination.lastModified() ||
                    source.length() != destination.length()) {
                FileUtils.copy(source, destination);
                result.add(destination);
            }
        }
        return result;
    }

    public static void copy(final File source, final File destination) throws IOException {
        final FileInputStream in = new FileInputStream(source);
        try {
            copy(in, destination);
            destination.setLastModified(source.lastModified());
        } finally {
            in.close();
        }
    }

    public static void copy(final File source, final OutputStream out) throws IOException {
        final FileInputStream in = new FileInputStream(source);
        try {
            copy(in, out);
        } finally {
            in.close();
        }
    }

    public static void copy(final InputStream in, final File destination) throws IOException {
        destination.getParentFile().mkdirs();
        final FileOutputStream out = new FileOutputStream(destination);
        try {
            copy(in, out);
        } finally {
            out.close();
        }
    }

    public static void copy(final InputStream in, final OutputStream out) throws IOException {
        final byte[] buf = new byte[1024];
        int length;
        while ((length = in.read(buf)) != -1) {
            out.write(buf, 0, length);
        }
    }

    public static boolean remove(final File file) throws IOException {
        if (file.isDirectory()) {
            final File[] files = file.listFiles();
            if (files != null) {
                for (final File temp : files) {
                    remove(temp);
                }
            }
        }
        return file.delete();
    }
}
